package math.base;

import java.util.Arrays;

/**
 * @author Saga
 *
 */
public class Matrix4d {
    public Matrix4d() {
        this.elements =
                new double[]{ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0,
                        0.0, 1.0, };
    }

    public Matrix4d(
            final double _a11, final double _a12, final double _a13, final double _a14,
            final double _a21, final double _a22, final double _a23, final double _a24,
            final double _a31, final double _a32, final double _a33, final double _a34,
            final double _a41, final double _a42, final double _a43, final double _a44) {
        this.elements = new double[]{
                _a11, _a21, _a31, _a41,
                _a12, _a22, _a32, _a42,
                _a13, _a23, _a33, _a43,
                _a14, _a24, _a34, _a44 };
    }

    public Matrix4d(final Coord4d _col1, final Coord4d _col2, final Coord4d _col3,
            final Coord4d _col4) {
        this.elements = new double[]{
                _col1.getX(), _col1.getY(), _col1.getZ(), _col1.getW(),
                _col2.getX(), _col2.getY(), _col2.getZ(), _col2.getW(),
                _col3.getX(), _col3.getY(), _col3.getZ(), _col3.getW(),
                _col4.getX(), _col4.getY(), _col4.getZ(), _col4.getW() };
    }

    //coord
    public final void setCoord(final int _rowId, final int _colId, final double _val) {
        switch (_rowId) {
        case 0: {
            switch (_colId) {
            case 0:
                this.elements[0] = _val;
                break;

            case 1:
                this.elements[4] = _val;
                break;

            case 2:
                this.elements[8] = _val;
                break;

            case 3:
                this.elements[12] = _val;

            default:
                throw new IndexOutOfBoundsException("");
            }
        }
        break;

        case 1: {
            switch (_colId) {
            case 0:
                this.elements[1] = _val;
                break;

            case 1:
                this.elements[5] = _val;
                break;

            case 2:
                this.elements[9] = _val;
                break;

            case 3:
                this.elements[13] = _val;

            default:
                throw new IndexOutOfBoundsException("");
            }
        }
        break;

        case 2: {
            switch (_colId) {
            case 0:
                this.elements[2] = _val;
                break;

            case 1:
                this.elements[6] = _val;
                break;

            case 2:
                this.elements[10] = _val;
                break;

            case 3:
                this.elements[14] = _val;
                break;

            default:
                throw new IndexOutOfBoundsException("");
            }
        }
        break;

        case 3: {
            switch (_colId) {
            case 0:
                this.elements[3] = _val;
                break;

            case 1:
                this.elements[7] = _val;
                break;

            case 2:
                this.elements[11] = _val;
                break;

            case 3:
                this.elements[15] = _val;
                break;

            default:
                throw new IndexOutOfBoundsException("");
            }
        }
        break;

        default:
            throw new IndexOutOfBoundsException("");
        }
    }

    public final double getCoord(final int _rowId, final int _colId) {
        switch (_rowId) {
        case 0: {
            switch (_colId) {
            case 0:
                return this.elements[0];

            case 1:
                return this.elements[4];

            case 2:
                return this.elements[8];

            case 3:
                return this.elements[12];

            default:
                throw new IndexOutOfBoundsException("");
            }
        }

        case 1: {
            switch (_colId) {
            case 0:
                return this.elements[1];

            case 1:
                return this.elements[5];

            case 2:
                return this.elements[9];

            case 3:
                return this.elements[13];

            default:
                throw new IndexOutOfBoundsException("");
            }
        }

        case 2: {
            switch (_colId) {
            case 0:
                return this.elements[2];

            case 1:
                return this.elements[6];

            case 2:
                return this.elements[10];

            case 3:
                return this.elements[14];

            default:
                throw new IndexOutOfBoundsException("");
            }
        }

        case 3: {
            switch (_colId) {
            case 0:
                return this.elements[3];

            case 1:
                return this.elements[7];

            case 2:
                return this.elements[11];

            case 3:
                return this.elements[15];

            default:
                throw new IndexOutOfBoundsException("");
            }
        }

        default:
            throw new IndexOutOfBoundsException("");
        }
    }

    public final void setRow(final int _id, final Coord4d _row) {
        if (_id < 0 || _id > 3) {
            throw new IndexOutOfBoundsException("");
        }

        this.elements[_id] = _row.getX();
        this.elements[_id + 4] = _row.getY();
        this.elements[_id + 8] = _row.getZ();
        this.elements[_id + 12] = _row.getW();
    }

    public final Coord4d getRow(final int _id) {
        if (_id < 0 || _id > 3) {
            throw new IndexOutOfBoundsException("");
        }

        return new Coord4d(this.elements[_id], this.elements[_id + 4], this.elements[_id + 8],
                this.elements[_id + 12]);
    }

    public final void setColumn(final int _id, final Coord4d _col) {
        if (_id < 0 || _id > 3) {
            throw new IndexOutOfBoundsException("");
        }

        final int pos = _id * 3;
        this.elements[pos] = _col.getX();
        this.elements[pos + 1] = _col.getY();
        this.elements[pos + 2] = _col.getZ();
        this.elements[pos + 3] = _col.getW();
    }

    public final Coord4d getColumn(final int _id) {
        if (_id < 0 || _id > 3) {
            throw new IndexOutOfBoundsException("");
        }

        final int pos = _id * 3;
        return new Coord4d(this.elements[pos], this.elements[pos + 1], this.elements[pos + 2],
                this.elements[pos + 3]);
    }

    public final void setIdentity() {
        this.elements[0] = 1.0;
        this.elements[1] = 0.0;
        this.elements[2] = 0.0;
        this.elements[3] = 0.0;
        this.elements[4] = 0.0;
        this.elements[5] = 1.0;
        this.elements[6] = 0.0;
        this.elements[7] = 0.0;
        this.elements[8] = 0.0;
        this.elements[9] = 0.0;
        this.elements[10] = 1.0;
        this.elements[11] = 0.0;
        this.elements[12] = 0.0;
        this.elements[13] = 0.0;
        this.elements[14] = 0.0;
        this.elements[15] = 1.0;
    }

    public static final Matrix4d createFromIdentity() {
        return new Matrix4d(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0,
                0.0, 1.0);
    }

    public final void setZero() {
        Arrays.fill(this.elements, 0.0);
    }

    public static final Matrix4d createFromZero() {
        return new Matrix4d(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                0.0, 0.0);
    }

    public final void setConstant(final double _val) {
        Arrays.fill(this.elements, _val);
    }

    public static final Matrix4d createFromConstant(final double _val) {
        return new Matrix4d(_val, _val, _val, _val, _val, _val, _val, _val, _val, _val, _val, _val,
                _val, _val, _val, _val);
    }

    public final void setDiagonal(final double _val) {
        this.elements[0] = _val;
        this.elements[1] = 0.0;
        this.elements[2] = 0.0;
        this.elements[3] = 0.0;
        this.elements[4] = 0.0;
        this.elements[5] = _val;
        this.elements[6] = 0.0;
        this.elements[7] = 0.0;
        this.elements[8] = 0.0;
        this.elements[9] = 0.0;
        this.elements[10] = _val;
        this.elements[11] = 0.0;
        this.elements[12] = 0.0;
        this.elements[13] = 0.0;
        this.elements[14] = 0.0;
        this.elements[15] = _val;
    }

    public static final Matrix4d createFromDiagonal(final double _val) {
        return new Matrix4d(_val, 0.0, 0.0, 0.0, 0.0, _val, 0.0, 0.0, 0.0, 0.0, _val, 0.0, 0.0, 0.0,
                0.0, _val);
    }

    public final void setDiagonal(final Coord4d _coord) {
        this.elements[0] = _coord.getX();
        this.elements[1] = 0.0;
        this.elements[2] = 0.0;
        this.elements[3] = 0.0;
        this.elements[4] = 0.0;
        this.elements[5] = _coord.getY();
        this.elements[6] = 0.0;
        this.elements[7] = 0.0;
        this.elements[8] = 0.0;
        this.elements[9] = 0.0;
        this.elements[10] = _coord.getZ();
        this.elements[11] = 0.0;
        this.elements[12] = 0.0;
        this.elements[13] = 0.0;
        this.elements[14] = 0.0;
        this.elements[15] = _coord.getW();
    }

    public static final Matrix4d createFromDiagonal(final Coord4d _coord) {
        return new Matrix4d(_coord.getX(), 0.0, 0.0, 0.0, 0.0, _coord.getY(), 0.0, 0.0, 0.0, 0.0,
                _coord.getZ(), 0.0, 0.0, 0.0, 0.0, _coord.getW());
    }

    //add
    public final void add(final Matrix4d _mat) {
        this.elements[0] += _mat.elements[0];
        this.elements[1] += _mat.elements[1];
        this.elements[2] += _mat.elements[2];
        this.elements[3] += _mat.elements[3];
        this.elements[4] += _mat.elements[4];
        this.elements[5] += _mat.elements[5];
        this.elements[6] += _mat.elements[6];
        this.elements[7] += _mat.elements[7];
        this.elements[8] += _mat.elements[8];
        this.elements[9] += _mat.elements[9];
        this.elements[10] += _mat.elements[10];
        this.elements[11] += _mat.elements[11];
        this.elements[12] += _mat.elements[12];
        this.elements[13] += _mat.elements[13];
        this.elements[14] += _mat.elements[14];
        this.elements[15] += _mat.elements[15];
    }

    public final Matrix4d added(final Matrix4d _mat) {
        return new Matrix4d(
                this.elements[0] + _mat.elements[0],
                this.elements[4] + _mat.elements[4],
                this.elements[8] + _mat.elements[8],
                this.elements[12] + _mat.elements[12],
                this.elements[1] + _mat.elements[1],
                this.elements[5] + _mat.elements[5],
                this.elements[9] + _mat.elements[9],
                this.elements[13] + _mat.elements[13],
                this.elements[2] + _mat.elements[2],
                this.elements[6] + _mat.elements[6],
                this.elements[10] + _mat.elements[10],
                this.elements[14] + _mat.elements[14],
                this.elements[3] + _mat.elements[3],
                this.elements[7] + _mat.elements[7],
                this.elements[11] + _mat.elements[11],
                this.elements[15] + _mat.elements[15]);
    }

    //subtract
    public final void subtract(final Matrix4d _mat) {
        this.elements[0] -= _mat.elements[0];
        this.elements[1] -= _mat.elements[1];
        this.elements[2] -= _mat.elements[2];
        this.elements[3] -= _mat.elements[3];
        this.elements[4] -= _mat.elements[4];
        this.elements[5] -= _mat.elements[5];
        this.elements[6] -= _mat.elements[6];
        this.elements[7] -= _mat.elements[7];
        this.elements[8] -= _mat.elements[8];
        this.elements[9] -= _mat.elements[9];
        this.elements[10] -= _mat.elements[10];
        this.elements[11] -= _mat.elements[11];
        this.elements[12] -= _mat.elements[12];
        this.elements[13] -= _mat.elements[13];
        this.elements[14] -= _mat.elements[14];
        this.elements[15] -= _mat.elements[15];
    }

    public final Matrix4d subtracted(final Matrix4d _mat) {
        return new Matrix4d(
                this.elements[0] - _mat.elements[0],
                this.elements[4] - _mat.elements[4],
                this.elements[8] - _mat.elements[8],
                this.elements[12] - _mat.elements[12],
                this.elements[1] - _mat.elements[1],
                this.elements[5] - _mat.elements[5],
                this.elements[9] - _mat.elements[9],
                this.elements[13] - _mat.elements[13],
                this.elements[2] - _mat.elements[2],
                this.elements[6] - _mat.elements[6],
                this.elements[10] - _mat.elements[10],
                this.elements[14] - _mat.elements[14],
                this.elements[3] - _mat.elements[3],
                this.elements[7] - _mat.elements[7],
                this.elements[11] - _mat.elements[11],
                this.elements[15] - _mat.elements[15]);
    }

    //reverse
    public final void reverse() {
        this.elements[0] = -this.elements[0];
        this.elements[1] = -this.elements[1];
        this.elements[2] = -this.elements[2];
        this.elements[3] = -this.elements[3];
        this.elements[4] = -this.elements[4];
        this.elements[5] = -this.elements[5];
        this.elements[6] = -this.elements[6];
        this.elements[7] = -this.elements[7];
        this.elements[8] = -this.elements[8];
        this.elements[9] = -this.elements[9];
        this.elements[10] = -this.elements[10];
        this.elements[11] = -this.elements[11];
        this.elements[12] = -this.elements[12];
        this.elements[13] = -this.elements[13];
        this.elements[14] = -this.elements[14];
        this.elements[15] = -this.elements[15];
    }

    public final Matrix4d reversed() {
        return new Matrix4d(-this.elements[0], -this.elements[4], -this.elements[8],
                -this.elements[12], -this.elements[1], -this.elements[5], -this.elements[9],
                -this.elements[13], -this.elements[2], -this.elements[6], -this.elements[10],
                -this.elements[14], -this.elements[3], -this.elements[7], -this.elements[11],
                -this.elements[15]);
    }

    //multiply,
    public final void multiply(final double _scalar) {
        this.elements[0] *= _scalar;
        this.elements[1] *= _scalar;
        this.elements[2] *= _scalar;
        this.elements[3] *= _scalar;
        this.elements[4] *= _scalar;
        this.elements[5] *= _scalar;
        this.elements[6] *= _scalar;
        this.elements[7] *= _scalar;
        this.elements[8] *= _scalar;
        this.elements[9] *= _scalar;
        this.elements[10] *= _scalar;
        this.elements[11] *= _scalar;
        this.elements[12] *= _scalar;
        this.elements[13] *= _scalar;
        this.elements[14] *= _scalar;
        this.elements[15] *= _scalar;
    }

    public final Matrix4d multiplied(final double _scalar) {
        return new Matrix4d(this.elements[0] * _scalar, this.elements[4] * _scalar,
                this.elements[8] * _scalar, this.elements[12] * _scalar, this.elements[1] * _scalar,
                this.elements[5] * _scalar, this.elements[9] * _scalar, this.elements[13] * _scalar,
                this.elements[2] * _scalar, this.elements[6] * _scalar, this.elements[10] * _scalar,
                this.elements[14] * _scalar, this.elements[3] * _scalar, this.elements[7] * _scalar,
                this.elements[11] * _scalar, this.elements[15] * _scalar);
    }

    public final void multiply(final Matrix4d _mat) {
        final double temp11 =
                this.elements[0] * _mat.elements[0] + this.elements[4] * _mat.elements[1] +
                        this.elements[8] * _mat.elements[2] + this.elements[12] * _mat.elements[3];
        final double temp12 =
                this.elements[0] * _mat.elements[4] + this.elements[4] * _mat.elements[5] +
                        this.elements[8] * _mat.elements[6] + this.elements[12] * _mat.elements[7];
        final double temp13 =
                this.elements[0] * _mat.elements[8] + this.elements[4] * _mat.elements[9] +
                        this.elements[8] * _mat.elements[10] +
                        this.elements[12] * _mat.elements[11];
        final double temp14 =
                this.elements[0] * _mat.elements[12] + this.elements[4] * _mat.elements[13] +
                        this.elements[8] * _mat.elements[14] +
                        this.elements[12] * _mat.elements[15];

        final double temp21 =
                this.elements[1] * _mat.elements[0] + this.elements[5] * _mat.elements[1] +
                        this.elements[9] * _mat.elements[2] + this.elements[13] * _mat.elements[3];
        final double temp22 =
                this.elements[1] * _mat.elements[4] + this.elements[5] * _mat.elements[5] +
                        this.elements[9] * _mat.elements[6] + this.elements[13] * _mat.elements[7];
        final double temp23 =
                this.elements[1] * _mat.elements[8] + this.elements[5] * _mat.elements[9] +
                        this.elements[9] * _mat.elements[10] +
                        this.elements[13] * _mat.elements[11];
        final double temp24 =
                this.elements[1] * _mat.elements[12] + this.elements[5] * _mat.elements[13] +
                        this.elements[9] * _mat.elements[14] +
                        this.elements[13] * _mat.elements[15];

        final double temp31 =
                this.elements[2] * _mat.elements[0] + this.elements[6] * _mat.elements[1] +
                        this.elements[10] * _mat.elements[2] + this.elements[14] * _mat.elements[3];
        final double temp32 =
                this.elements[2] * _mat.elements[4] + this.elements[6] * _mat.elements[5] +
                        this.elements[10] * _mat.elements[6] + this.elements[14] * _mat.elements[7];
        final double temp33 =
                this.elements[2] * _mat.elements[8] + this.elements[6] * _mat.elements[9] +
                        this.elements[10] * _mat.elements[10] +
                        this.elements[14] * _mat.elements[11];
        final double temp34 =
                this.elements[2] * _mat.elements[12] + this.elements[6] * _mat.elements[13] +
                        this.elements[10] * _mat.elements[14] +
                        this.elements[14] * _mat.elements[15];

        final double temp41 =
                this.elements[3] * _mat.elements[0] + this.elements[7] * _mat.elements[1] +
                        this.elements[11] * _mat.elements[2] + this.elements[15] * _mat.elements[3];
        final double temp42 =
                this.elements[3] * _mat.elements[4] + this.elements[7] * _mat.elements[5] +
                        this.elements[11] * _mat.elements[6] + this.elements[15] * _mat.elements[7];
        final double temp43 =
                this.elements[3] * _mat.elements[8] + this.elements[7] * _mat.elements[9] +
                        this.elements[11] * _mat.elements[10] +
                        this.elements[15] * _mat.elements[11];
        this.elements[15] =
                this.elements[3] * _mat.elements[12] + this.elements[7] * _mat.elements[13] +
                        this.elements[11] * _mat.elements[14] +
                        this.elements[15] * _mat.elements[15];

        this.elements[0] = temp11;
        this.elements[1] = temp21;
        this.elements[2] = temp31;
        this.elements[3] = temp41;
        this.elements[4] = temp12;
        this.elements[5] = temp22;
        this.elements[6] = temp32;
        this.elements[7] = temp42;
        this.elements[8] = temp13;
        this.elements[9] = temp23;
        this.elements[10] = temp33;
        this.elements[11] = temp43;
        this.elements[12] = temp14;
        this.elements[13] = temp24;
        this.elements[14] = temp34;
    }

    public final Matrix4d multiplied(final Matrix4d _mat) {
        final double temp11 =
                this.elements[0] * _mat.elements[0] + this.elements[4] * _mat.elements[1] +
                        this.elements[8] * _mat.elements[2] + this.elements[12] * _mat.elements[3];
        final double temp12 =
                this.elements[0] * _mat.elements[4] + this.elements[4] * _mat.elements[5] +
                        this.elements[8] * _mat.elements[6] + this.elements[12] * _mat.elements[7];
        final double temp13 =
                this.elements[0] * _mat.elements[8] + this.elements[4] * _mat.elements[9] +
                        this.elements[8] * _mat.elements[10] +
                        this.elements[12] * _mat.elements[11];
        final double temp14 =
                this.elements[0] * _mat.elements[12] + this.elements[4] * _mat.elements[13] +
                        this.elements[8] * _mat.elements[14] +
                        this.elements[12] * _mat.elements[15];

        final double temp21 =
                this.elements[1] * _mat.elements[0] + this.elements[5] * _mat.elements[1] +
                        this.elements[9] * _mat.elements[2] + this.elements[13] * _mat.elements[3];
        final double temp22 =
                this.elements[1] * _mat.elements[4] + this.elements[5] * _mat.elements[5] +
                        this.elements[9] * _mat.elements[6] + this.elements[13] * _mat.elements[7];
        final double temp23 =
                this.elements[1] * _mat.elements[8] + this.elements[5] * _mat.elements[9] +
                        this.elements[9] * _mat.elements[10] +
                        this.elements[13] * _mat.elements[11];
        final double temp24 =
                this.elements[1] * _mat.elements[12] + this.elements[5] * _mat.elements[13] +
                        this.elements[9] * _mat.elements[14] +
                        this.elements[13] * _mat.elements[15];

        final double temp31 =
                this.elements[2] * _mat.elements[0] + this.elements[6] * _mat.elements[1] +
                        this.elements[10] * _mat.elements[2] + this.elements[14] * _mat.elements[3];
        final double temp32 =
                this.elements[2] * _mat.elements[4] + this.elements[6] * _mat.elements[5] +
                        this.elements[10] * _mat.elements[6] + this.elements[14] * _mat.elements[7];
        final double temp33 =
                this.elements[2] * _mat.elements[8] + this.elements[6] * _mat.elements[9] +
                        this.elements[10] * _mat.elements[10] +
                        this.elements[14] * _mat.elements[11];
        final double temp34 =
                this.elements[2] * _mat.elements[12] + this.elements[6] * _mat.elements[13] +
                        this.elements[10] * _mat.elements[14] +
                        this.elements[14] * _mat.elements[15];

        final double temp41 =
                this.elements[3] * _mat.elements[0] + this.elements[7] * _mat.elements[1] +
                        this.elements[11] * _mat.elements[2] + this.elements[15] * _mat.elements[3];
        final double temp42 =
                this.elements[3] * _mat.elements[4] + this.elements[7] * _mat.elements[5] +
                        this.elements[11] * _mat.elements[6] + this.elements[15] * _mat.elements[7];
        final double temp43 =
                this.elements[3] * _mat.elements[8] + this.elements[7] * _mat.elements[9] +
                        this.elements[11] * _mat.elements[10] +
                        this.elements[15] * _mat.elements[11];
        final double temp44 =
                this.elements[3] * _mat.elements[12] + this.elements[7] * _mat.elements[13] +
                        this.elements[11] * _mat.elements[14] +
                        this.elements[15] * _mat.elements[15];

        return new Matrix4d(temp11, temp12, temp13, temp14, temp21, temp22, temp23, temp24, temp31,
                temp32, temp33, temp34, temp41, temp42, temp43, temp44);
    }

    public final void preMultiply(final Matrix4d _mat) {
        final double temp11 =
                _mat.elements[0] * this.elements[0] + _mat.elements[4] * this.elements[1] +
                        _mat.elements[8] * this.elements[2] + _mat.elements[12] * this.elements[3];
        final double temp12 =
                _mat.elements[0] * this.elements[4] + _mat.elements[4] * this.elements[5] +
                        _mat.elements[8] * this.elements[6] + _mat.elements[12] * this.elements[7];
        final double temp13 =
                _mat.elements[0] * this.elements[8] + _mat.elements[4] * this.elements[9] +
                        _mat.elements[8] * this.elements[10] +
                        _mat.elements[12] * this.elements[11];
        final double temp14 =
                _mat.elements[0] * this.elements[12] + _mat.elements[4] * this.elements[13] +
                        _mat.elements[8] * this.elements[14] +
                        _mat.elements[12] * this.elements[15];

        final double temp21 =
                _mat.elements[1] * this.elements[0] + _mat.elements[5] * this.elements[1] +
                        _mat.elements[9] * this.elements[2] + _mat.elements[13] * this.elements[3];
        final double temp22 =
                _mat.elements[1] * this.elements[4] + _mat.elements[5] * this.elements[5] +
                        _mat.elements[9] * this.elements[6] + _mat.elements[13] * this.elements[7];
        final double temp23 =
                _mat.elements[1] * this.elements[8] + _mat.elements[5] * this.elements[9] +
                        _mat.elements[9] * this.elements[10] +
                        _mat.elements[13] * this.elements[11];
        final double temp24 =
                _mat.elements[1] * this.elements[12] + _mat.elements[5] * this.elements[13] +
                        _mat.elements[9] * this.elements[14] +
                        _mat.elements[13] * this.elements[15];

        final double temp31 =
                _mat.elements[2] * this.elements[0] + _mat.elements[6] * this.elements[1] +
                        _mat.elements[10] * this.elements[2] + _mat.elements[14] * this.elements[3];
        final double temp32 =
                _mat.elements[2] * this.elements[4] + _mat.elements[6] * this.elements[5] +
                        _mat.elements[10] * this.elements[6] + _mat.elements[14] * this.elements[7];
        final double temp33 =
                _mat.elements[2] * this.elements[8] + _mat.elements[6] * this.elements[9] +
                        _mat.elements[10] * this.elements[10] +
                        _mat.elements[14] * this.elements[11];
        final double temp34 =
                _mat.elements[2] * this.elements[12] + _mat.elements[6] * this.elements[13] +
                        _mat.elements[10] * this.elements[14] +
                        _mat.elements[14] * this.elements[15];

        final double temp41 =
                _mat.elements[3] * this.elements[0] + _mat.elements[7] * this.elements[1] +
                        _mat.elements[11] * this.elements[2] + _mat.elements[15] * this.elements[3];
        final double temp42 =
                _mat.elements[3] * this.elements[4] + _mat.elements[7] * this.elements[5] +
                        _mat.elements[11] * this.elements[6] + _mat.elements[15] * this.elements[7];
        final double temp43 =
                _mat.elements[3] * this.elements[8] + _mat.elements[7] * this.elements[9] +
                        _mat.elements[11] * this.elements[10] +
                        _mat.elements[15] * this.elements[11];
        this.elements[15] =
                _mat.elements[3] * this.elements[12] + _mat.elements[7] * this.elements[13] +
                        _mat.elements[11] * this.elements[14] +
                        _mat.elements[15] * this.elements[15];

        this.elements[0] = temp11;
        this.elements[1] = temp21;
        this.elements[2] = temp31;
        this.elements[3] = temp41;
        this.elements[4] = temp12;
        this.elements[5] = temp22;
        this.elements[6] = temp32;
        this.elements[7] = temp42;
        this.elements[8] = temp13;
        this.elements[9] = temp23;
        this.elements[10] = temp33;
        this.elements[11] = temp43;
        this.elements[12] = temp14;
        this.elements[13] = temp24;
        this.elements[14] = temp34;
    }

    public final Matrix4d preMultiplied(final Matrix4d _mat) {
        final double temp11 =
                _mat.elements[0] * this.elements[0] + _mat.elements[4] * this.elements[1] +
                        _mat.elements[8] * this.elements[2] + _mat.elements[12] * this.elements[3];
        final double temp12 =
                _mat.elements[0] * this.elements[4] + _mat.elements[4] * this.elements[5] +
                        _mat.elements[8] * this.elements[6] + _mat.elements[12] * this.elements[7];
        final double temp13 =
                _mat.elements[0] * this.elements[8] + _mat.elements[4] * this.elements[9] +
                        _mat.elements[8] * this.elements[10] +
                        _mat.elements[12] * this.elements[11];
        final double temp14 =
                _mat.elements[0] * this.elements[12] + _mat.elements[4] * this.elements[13] +
                        _mat.elements[8] * this.elements[14] +
                        _mat.elements[12] * this.elements[15];

        final double temp21 =
                _mat.elements[1] * this.elements[0] + _mat.elements[5] * this.elements[1] +
                        _mat.elements[9] * this.elements[2] + _mat.elements[13] * this.elements[3];
        final double temp22 =
                _mat.elements[1] * this.elements[4] + _mat.elements[5] * this.elements[5] +
                        _mat.elements[9] * this.elements[6] + _mat.elements[13] * this.elements[7];
        final double temp23 =
                _mat.elements[1] * this.elements[8] + _mat.elements[5] * this.elements[9] +
                        _mat.elements[9] * this.elements[10] +
                        _mat.elements[13] * this.elements[11];
        final double temp24 =
                _mat.elements[1] * this.elements[12] + _mat.elements[5] * this.elements[13] +
                        _mat.elements[9] * this.elements[14] +
                        _mat.elements[13] * this.elements[15];

        final double temp31 =
                _mat.elements[2] * this.elements[0] + _mat.elements[6] * this.elements[1] +
                        _mat.elements[10] * this.elements[2] + _mat.elements[14] * this.elements[3];
        final double temp32 =
                _mat.elements[2] * this.elements[4] + _mat.elements[6] * this.elements[5] +
                        _mat.elements[10] * this.elements[6] + _mat.elements[14] * this.elements[7];
        final double temp33 =
                _mat.elements[2] * this.elements[8] + _mat.elements[6] * this.elements[9] +
                        _mat.elements[10] * this.elements[10] +
                        _mat.elements[14] * this.elements[11];
        final double temp34 =
                _mat.elements[2] * this.elements[12] + _mat.elements[6] * this.elements[13] +
                        _mat.elements[10] * this.elements[14] +
                        _mat.elements[14] * this.elements[15];

        final double temp41 =
                _mat.elements[3] * this.elements[0] + _mat.elements[7] * this.elements[1] +
                        _mat.elements[11] * this.elements[2] + _mat.elements[15] * this.elements[3];
        final double temp42 =
                _mat.elements[3] * this.elements[4] + _mat.elements[7] * this.elements[5] +
                        _mat.elements[11] * this.elements[6] + _mat.elements[15] * this.elements[7];
        final double temp43 =
                _mat.elements[3] * this.elements[8] + _mat.elements[7] * this.elements[9] +
                        _mat.elements[11] * this.elements[10] +
                        _mat.elements[15] * this.elements[11];
        final double temp44 =
                _mat.elements[3] * this.elements[12] + _mat.elements[7] * this.elements[13] +
                        _mat.elements[11] * this.elements[14] +
                        _mat.elements[15] * this.elements[15];

        return new Matrix4d(temp11, temp12, temp13, temp14, temp21, temp22, temp23, temp24, temp31,
                temp32, temp33, temp34, temp41, temp42, temp43, temp44);
    }

    public final Coord4d multiplied(final Coord4d _coord) {
        final double tempX = this.elements[0] * _coord.getX() + this.elements[4] * _coord.getY() +
                this.elements[8] * _coord.getZ() + this.elements[12] * _coord.getW();
        final double tempY = this.elements[1] * _coord.getX() + this.elements[5] * _coord.getY() +
                this.elements[9] * _coord.getZ() + this.elements[13] * _coord.getW();
        final double tempZ = this.elements[2] * _coord.getX() + this.elements[6] * _coord.getY() +
                this.elements[10] * _coord.getZ() + this.elements[14] * _coord.getW();
        final double tempW = this.elements[3] * _coord.getX() + this.elements[7] * _coord.getY() +
                this.elements[11] * _coord.getZ() + this.elements[15] * _coord.getW();

        return new Coord4d(tempX, tempY, tempZ, tempW);
    }

    public final Coord4d preMultiplied(final Coord4d _coord) {
        final double tempX = this.elements[0] * _coord.getX() + this.elements[1] * _coord.getY() +
                this.elements[2] * _coord.getZ() + this.elements[3] * _coord.getW();
        final double tempY = this.elements[4] * _coord.getX() + this.elements[5] * _coord.getY() +
                this.elements[6] * _coord.getZ() + this.elements[7] * _coord.getW();
        final double tempZ = this.elements[8] * _coord.getX() + this.elements[9] * _coord.getY() +
                this.elements[10] * _coord.getZ() + this.elements[11] * _coord.getW();
        final double tempW = this.elements[12] * _coord.getX() + this.elements[13] * _coord.getY() +
                this.elements[14] * _coord.getZ() + this.elements[15] * _coord.getW();

        return new Coord4d(tempX, tempY, tempZ, tempW);
    }

    //divide
    public final void divide(final double _denom) {
        final double scalar = 1.0 / _denom;
        this.elements[0] *= scalar;
        this.elements[1] *= scalar;
        this.elements[2] *= scalar;
        this.elements[3] *= scalar;
        this.elements[4] *= scalar;
        this.elements[5] *= scalar;
        this.elements[6] *= scalar;
        this.elements[7] *= scalar;
        this.elements[8] *= scalar;
        this.elements[9] *= scalar;
        this.elements[10] *= scalar;
        this.elements[11] *= scalar;
        this.elements[12] *= scalar;
        this.elements[13] *= scalar;
        this.elements[14] *= scalar;
        this.elements[15] *= scalar;
    }

    public final Matrix4d divided(final double _denom) {
        final double scalar = 1.0 / _denom;
        return new Matrix4d(this.elements[0] * scalar, this.elements[4] * scalar,
                this.elements[8] * scalar, this.elements[12] * scalar, this.elements[1] * scalar,
                this.elements[5] * scalar, this.elements[9] * scalar, this.elements[13] * scalar,
                this.elements[2] * scalar, this.elements[6] * scalar, this.elements[10] * scalar,
                this.elements[14] * scalar, this.elements[3] * scalar, this.elements[7] * scalar,
                this.elements[11] * scalar, this.elements[15] * scalar);
    }

    //tranpose
    public final void transpose() {
        double temp = this.elements[1];
        this.elements[1] = this.elements[4];
        this.elements[4] = temp;

        temp = this.elements[2];
        this.elements[2] = this.elements[8];
        this.elements[8] = temp;

        temp = this.elements[3];
        this.elements[3] = this.elements[12];
        this.elements[12] = temp;

        temp = this.elements[6];
        this.elements[6] = this.elements[9];
        this.elements[9] = temp;

        temp = this.elements[7];
        this.elements[7] = this.elements[13];
        this.elements[13] = temp;

        temp = this.elements[11];
        this.elements[11] = this.elements[14];
        this.elements[14] = temp;
    }

    public final Matrix4d transposed() {
        return new Matrix4d(this.elements[0], this.elements[1], this.elements[2], this.elements[3],
                this.elements[4], this.elements[5], this.elements[6], this.elements[7],
                this.elements[8], this.elements[9], this.elements[10], this.elements[11],
                this.elements[12], this.elements[13], this.elements[14], this.elements[15]);
    }

    //invert
    public final void invert() {
    }

    public final Matrix4d inverted() {

        return null;
    }

    public final void power(final int _order) {
        switch (_order) {
        case 1:
            return;

        case 0:
            this.setIdentity();
            return;

        case -1:
            this.invert();
            return;

        default: {
            if (_order < 0) {
                this.invert();
            }

            int tempOrder = _order;
            if (tempOrder < 0) {
                tempOrder = -tempOrder;
            }
            --tempOrder;

            final Matrix4d temp = this.clone();
            for (; ; ) {
                if (tempOrder % 2 == 1) {
                    this.multiply(temp);
                }
                if (tempOrder == 1) {
                    return;
                }
                temp.multiply(temp);
                tempOrder >>= 1;
            }
        }
        }
    }

    public final Matrix4d powered(final int _order) {
        switch (_order) {
        case 1:
            return this.clone();

        case 0:
            return createFromIdentity();

        case -1:
            return this.inverted();

        default: {
            Matrix4d result = this.clone();
            if (_order < 0) {
                result = this.inverted();
            }

            int tempOrder = _order;
            if (tempOrder < 0) {
                tempOrder = -tempOrder;
            }
            --tempOrder;

            final Matrix4d temp = result.clone();
            for (; ; ) {
                if (tempOrder % 2 == 1) {
                    result.multiply(temp);
                }
                if (tempOrder == 1) {
                    return result;
                }
                temp.multiply(temp);
                tempOrder >>= 1;
            }
        }
        }
    }

    //attribute
    public final double calcTrace() {
        return this.elements[0] + this.elements[5] + this.elements[10] + this.elements[15];
    }

    public final double calcDeterminant() {
        final double temp813 = this.elements[8] * this.elements[13];
        final double temp814 = this.elements[8] * this.elements[14];
        final double temp815 = this.elements[8] * this.elements[15];
        final double temp912 = this.elements[9] * this.elements[12];
        final double temp914 = this.elements[9] * this.elements[14];
        final double temp915 = this.elements[9] * this.elements[15];
        final double temp1012 = this.elements[10] * this.elements[12];
        final double temp1013 = this.elements[10] * this.elements[13];
        final double temp1015 = this.elements[10] * this.elements[15];
        final double temp1112 = this.elements[11] * this.elements[12];
        final double temp1113 = this.elements[11] * this.elements[13];
        final double temp1114 = this.elements[11] * this.elements[14];

        return this.elements[0] * (this.elements[5] * (temp1015 - temp1114) +
                this.elements[6] * (temp1113 - temp915) + this.elements[7] * (temp914 - temp1013)) -
                this.elements[1] * (this.elements[4] * (temp1015 - temp1114) +
                        this.elements[6] * (temp1112 - temp815) +
                        this.elements[7] * (temp814 - temp1012)) + this.elements[2] *
                (this.elements[4] * (temp915 - temp1113) + this.elements[5] * (temp1112 - temp815) +
                        this.elements[7] * (temp813 - temp912)) - this.elements[3] *
                (this.elements[4] * (temp914 - temp1013) + this.elements[5] * (temp1012 - temp814) +
                        this.elements[6] * (temp813 - temp912));
    }

    @Override
    public final Matrix4d clone() {
        return new Matrix4d(this.elements[0], this.elements[4], this.elements[8], this.elements[12],
                this.elements[1], this.elements[5], this.elements[9], this.elements[13],
                this.elements[2], this.elements[6], this.elements[10], this.elements[14],
                this.elements[3], this.elements[7], this.elements[11], this.elements[15]);
    }

    public double[] elements;
}
